package android.magical;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.magical.CalendarFragment.ViewHolder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends FragmentActivity implements OnPageChangeListener {
	private final static String TAG = "MainActivity";
	public static final int NUM_ITEMS = 400;
	public static int initialPosition = NUM_ITEMS / 2;
	private static final int REQUEST_DISCOVERABLE_BLUETOOTH = 1;
	private MyAdapter mAdapter;
	private DatabaseControl db;
	private ViewPager mPager;
	private TextView monthLabel;
	private ImageButton bluetooth;
	private GregorianCalendar mCalendar;
	private int mLastPosition;
	private boolean mDatePassed;
	private GregorianCalendar mChosenDate;
	public static final int POST_MESSAGE = 0;
	private BluetoothAdapter mBTAdapter = BluetoothAdapter.getDefaultAdapter();
	private MagiCalBT magicBT;
	public static SharedPreferences preference; 
	private String mOwnerName;
	public static View lastChosenDay;
	private ArrayList<View> thisMonthDays = new ArrayList<View>();
	private ArrayList<View> nextMonthDays = new ArrayList<View>();
	private ArrayList<View> prevMonthDays = new ArrayList<View>();
	private boolean nextMonthFull = false;
	private boolean thisMonthFull = false;
	private boolean prevMonthFull = false;
	private EventsFragment mEvents;

	private final Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {

			case POST_MESSAGE:
				String writeMessage = new String((byte[]) msg.obj);
				String[] info = writeMessage.split("&");

				db.addItem(info[2], info[0], info[1], info[3].trim());
				MyAdapter adapter = (MyAdapter)mPager.getAdapter();
				adapter.notifyDataSetChanged();
				Toast.makeText(MainActivity.this, "Event added from "+info[2]+" on "+info[0]+" at "+info[1]+" : "+info[3].trim(), Toast.LENGTH_LONG).show();
				break;
			}
		}
	};
	
	@Override
	public void onConfigurationChanged (Configuration newConfig){
		super.onConfigurationChanged(newConfig);
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
	}

	public void savePreference(String owner){
		SharedPreferences.Editor editPreference= preference.edit();
		editPreference.putString("Owner",owner);
		editPreference.commit();
		mOwnerName=preference.getString("Owner","null");
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
		Log.v(TAG, "Started");
		magicBT = new MagiCalBT(mHandler);

		// Get layout elements
		mPager = (ViewPager)findViewById(R.id.pager);
		ImageButton add = (ImageButton)findViewById(R.id.add_event);
		bluetooth = (ImageButton)findViewById(R.id.bluetooth);
		ImageButton prev = (ImageButton)findViewById(R.id.prev_month);
		ImageButton next = (ImageButton)findViewById(R.id.next_month);
		monthLabel = (TextView)findViewById(R.id.month_label);
		mEvents = (EventsFragment)getSupportFragmentManager().findFragmentById(R.id.events_fragment);
		db = new DatabaseControl(this);
		db.open();

		mCalendar = new GregorianCalendar();
		mChosenDate = mCalendar;
		FragmentManager fm = getSupportFragmentManager();
		mEvents.showEvents(mChosenDate.get(Calendar.DAY_OF_MONTH), mChosenDate.get(Calendar.MONTH), mChosenDate.get(Calendar.YEAR));


		// Intialise ViewPager
		intialiseViewPager();

		// Add event button
		add.setOnClickListener(new OnClickListener(){
			public void onClick(View v){
				FragmentManager fm = getSupportFragmentManager();
				FragmentTransaction tr = fm.beginTransaction();
				
				// Hide main content
				View content = findViewById(R.id.main_content);
				Animation out = AnimationUtils.makeOutAnimation(MainActivity.this, true);
				content.startAnimation(out);
				content.setVisibility(View.GONE);
				
				// Hide events
				View events = findViewById(R.id.events_fragment);
				events.setVisibility(View.GONE);
				
				// Show AddEventFragment
				tr.add(R.id.event_container, new AddEventFragment(), "addEvent");
				tr.addToBackStack("addEvent");
				tr.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
				tr.commit();
			}
		});

		// Setup indicator depending on bluetooth status
		if(mBTAdapter.isEnabled()) 
			bluetooth.setImageResource(R.drawable.bluetooth_button_activated);
		else 
			bluetooth.setImageResource(R.drawable.bluetooth_button);

		// Bluetooth button
		bluetooth.setOnClickListener(new OnClickListener(){
			public void onClick(View v){
				
				// No bluetooth device present
				if (mBTAdapter == null) {
					Log.i(TAG, "NO Bluetooth device");
					finish();
					return;

				}

				// true if bluetooth is available and enabled
				// false if bluetooth service is off
				boolean availableBT = (mBTAdapter != null && mBTAdapter.isEnabled());
				
				// Turn off if the bluetooth is on
				if (availableBT) {
					mBTAdapter.disable();
					((ImageButton)v).setImageResource(R.drawable.bluetooth_button);
				}
				// Turn on bluetooth and visibility
				else if (availableBT == false) {
					boolean success = mBTAdapter.enable();// the system will switch it on automatically
					// Request to enable local bluetooth device discoverability
					if(success) ((ImageButton)v).setImageResource(R.drawable.bluetooth_button_activated);
					Set<BluetoothDevice> btDeviceset = mBTAdapter.getBondedDevices();
					// Get the list of device that has been paired already
					if (btDeviceset.size() > 0) { // if the list has paired devices
						for (Iterator<BluetoothDevice> iterator = btDeviceset.iterator(); iterator
								.hasNext();) {
							BluetoothDevice bluetoothDevice = (BluetoothDevice) iterator
									.next();
							// it will get the address of all the devices.
						}

					}
					Intent discoverableIntent = new Intent(
							BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

					// Default visible time is 120 seconds.
					// But for debug purpose, we give it a longer visible time of 1000
					// seconds
					discoverableIntent.putExtra(
							BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 1000);
					startActivityForResult(discoverableIntent,
							REQUEST_DISCOVERABLE_BLUETOOTH);
				}

			}
		});        

		// Previous month button
		prev.setOnClickListener(new OnClickListener(){
			public void onClick(View v){
				mPager.setCurrentItem(mPager.getCurrentItem()-1, true);
			}
		});

		prev.setOnLongClickListener(new OnLongClickListener(){
			@Override
			public boolean onLongClick(View v) {
				if(mLastPosition > initialPosition){
					
					// Go back to current month
					mPager.setCurrentItem(initialPosition, true);
					mCalendar = new GregorianCalendar();
					updateMonthLabel();
					mLastPosition = mPager.getCurrentItem();
					return true;
				}
				return false;
			}
		});

		// Next month button
		next.setOnClickListener(new OnClickListener(){
			public void onClick(View v){
				mPager.setCurrentItem(mPager.getCurrentItem()+1, true);	
			}
		});

		next.setOnLongClickListener(new OnLongClickListener(){
			@Override
			public boolean onLongClick(View v) {
				if(mLastPosition < initialPosition){
					
					// Go back to current month
					mPager.setCurrentItem(initialPosition, true);
					mCalendar = new GregorianCalendar();
					updateMonthLabel();
					mLastPosition = mPager.getCurrentItem();
					return true;
				}
				return false;
			}
		});

		// Get username if present
		preference= this.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
		String ownerName = preference.getString("Owner",null);
		if (ownerName == null){
			DisplayOwnerFragment dio = DisplayOwnerFragment.newInstance();
			dio.show(getSupportFragmentManager(), "dialog");
		}
		else {
			mOwnerName = preference.getString("Owner",null);
		}
	}

	@Override
	public void onBackPressed(){
		restoreMainView();
		super.onBackPressed();
	}

	public void restoreMainView(){
		FragmentManager fm = getSupportFragmentManager();
		if(fm.getBackStackEntryCount() > 0){
			String lastFrag = fm.getBackStackEntryAt(fm.getBackStackEntryCount()-1).getName();
			if(lastFrag.equals("viewEvent") || lastFrag.equals("addEvent")){
				Log.v(TAG, "Setting Main content to be visible again");
				updateEventsLabel("Events");
				
				// Show main content
				Animation in = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
				View content = findViewById(R.id.main_content);
				content.startAnimation(in);
				content.setVisibility(View.VISIBLE);
				
				// Show events fragment

				mEvents.showEvents(mChosenDate.get(Calendar.DAY_OF_MONTH), 
										mChosenDate.get(Calendar.MONTH), 
										mChosenDate.get(Calendar.YEAR));
				View eventsView = findViewById(R.id.events_fragment);
				eventsView.setVisibility(View.VISIBLE);
				

			}
		}
	}

	public EventsFragment getEventsFragment(){
		return mEvents;
	}
	
	public void setEventsFragment(EventsFragment frag){
		mEvents = frag;
	}
	
	@Override
	public void onResume(){
		super.onResume();
		Log.v(TAG, "open database if it's not open");
		if(!db.isOpen()) db.open();

		updateMonthLabel();
		// Setup indicator depending on bluetooth status
		if(mBTAdapter.isEnabled()) 
			bluetooth.setImageResource(R.drawable.bluetooth_button_activated);
		else 
			bluetooth.setImageResource(R.drawable.bluetooth_button);
	}

	@Override
	public void onStop(){
		Log.v(TAG, "closing db");
		db.close();
		super.onStop();
	}

	private void intialiseViewPager() {
		Log.v(TAG, "Initialising the ViewPager");
		mAdapter  = new MyAdapter(getSupportFragmentManager(), mCalendar.getTimeInMillis());
		mPager.setAdapter(this.mAdapter);
		
		// Set current item to be the middle
		mPager.setCurrentItem(initialPosition, true);
		mPager.setOnPageChangeListener(this);
		mLastPosition = mPager.getCurrentItem();
	}

	private void updateMonthLabel(){
		monthLabel.setText(getMonthName(mCalendar.get(GregorianCalendar.MONTH)) + " " + 
										mCalendar.get(GregorianCalendar.YEAR));
	}

	public void updateEventsLabel(String title){
		TextView eventsLabel = (TextView)findViewById(R.id.events_label);
		eventsLabel.setText(title);
	}

	public void onPageScrollStateChanged(int arg0) {}

	public void onPageScrolled(int position, float positionOffset,
			int positionOffsetPixels) {}

	public void onPageSelected(int position) {
		Log.v(TAG, "position: " + position);
		//FragmentManager fm = getSupportFragmentManager();
		//mEvents = (EventsFragment) fm.findFragmentById(R.id.events_fragment);
		
		// Calendar days are already created by CalendarFragment
		nextMonthFull = true;
		thisMonthFull = true;
		prevMonthFull = true;
		if(position > mLastPosition){
			mChosenDate.add(Calendar.MONTH, 1);
			mCalendar.add(GregorianCalendar.MONTH, 1);
			mLastPosition++;
			
			// This month days become previous month days
			prevMonthDays = new ArrayList<View>(thisMonthDays);
			
			// Next month days become this month days
			thisMonthDays = new ArrayList<View>(nextMonthDays);
		} else {
			mChosenDate.add(Calendar.MONTH, -1);
			mCalendar.add(GregorianCalendar.MONTH, -1);
			mLastPosition--;
			
			// This month days become next month days
			nextMonthDays = new ArrayList<View>(thisMonthDays);
			
			// Previous month days become this month days
			thisMonthDays = new ArrayList<View>(prevMonthDays);
		}
		
		// Last chosen day back to normal
		MainActivity.lastChosenDay.setBackgroundResource(R.drawable.day_borders);
		
		// Highlight the day
		if (thisMonthDays.size()>mChosenDate.get(Calendar.DAY_OF_MONTH)){
			View highlightDay = thisMonthDays.get(mChosenDate.get(Calendar.DAY_OF_MONTH)-1);
			if (Integer.valueOf(((ViewHolder)highlightDay.getTag()).idDay.getText().toString())==mChosenDate.get(Calendar.DAY_OF_MONTH)){
				MainActivity.lastChosenDay = highlightDay;
				MainActivity.lastChosenDay.setBackgroundResource(R.drawable.day_borders_after_click);
			}
		}
		
		// Show events if there are any for the selected month and day
		if(mChosenDate.get(Calendar.MONTH) == mCalendar.get(Calendar.MONTH)){
			mEvents.showEvents(mChosenDate.get(Calendar.DAY_OF_MONTH), 
					mChosenDate.get(Calendar.MONTH), 
					mChosenDate.get(Calendar.YEAR));
		}
		else mEvents.setListAdapter(null);
		
		updateMonthLabel();
	}
	
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		
		switch (requestCode) {
		case REQUEST_DISCOVERABLE_BLUETOOTH:

			if (resultCode >=120) {// success, result code is device visible
				// time we set

				//	Toast.makeText(this, "BT enabled", Toast.LENGTH_LONG).show();

				Log.i(TAG, "Bluetooth is running");

				//	setTitle("Bluetooth is running");
				/*
				 * if (mAcceptThread == null) { mAcceptThread = new
				 * AcceptThread(); mAcceptThread.start(); }
				 */
				magicBT.accept(mBTAdapter);// accepting bluetooth connection

			} else {// failed to switch on device discovery

					Toast.makeText(this, "Bluetooth / visibility not enabled, please try again.", Toast.LENGTH_LONG)
							.show();
				finish();
			}
			break;
		}
	}

	public static class MyAdapter extends FragmentStatePagerAdapter {
		private HashMap<Integer, Fragment> mPageReferenceMap = new HashMap<Integer, Fragment>();

		public MyAdapter(FragmentManager fm, long mDate) {
			super(fm);
		}

		@Override
		public int getCount() {
			return NUM_ITEMS;
		}

		@Override
		public int getItemPosition(Object object) {
			return POSITION_NONE;
		}

		@Override
		public Fragment getItem(int position) {
			// Generates a fragment CalendarFragment for the view pager
			Log.v("position", ""+position);
			CalendarFragment calendar = CalendarFragment.newInstance(position);
			Log.v(TAG, "putting fragment into map");
			mPageReferenceMap.put(position, calendar);
			return calendar;
		}

		public CalendarFragment getFragment(int key) {
			return (CalendarFragment) mPageReferenceMap.get(key);
		}
		@SuppressWarnings("deprecation")
		@Override
		public void destroyItem(View container, int position, Object object) {
			super.destroyItem(container, position, object);
			mPageReferenceMap.remove(position);
		}
	}
	
	
	// GETTER / SETTER METHODS
	
	public void setChosenDate(int day, int month, int year){
		mChosenDate = new GregorianCalendar(year, month, day);
	}

	public GregorianCalendar getChosenDate(){
		return mChosenDate;
	}

	public GregorianCalendar getmCalendar(){
		return mCalendar;
	}

	public ViewPager getPager() {
		return mPager;
	}

	public DatabaseControl getDatabase(){
		return db;
	}

	public void setDatePassed(boolean passed){
		mDatePassed = passed;
	}

	public boolean isDatePassed(){
		return mDatePassed;
	}
	
	public String getOwnerName(){
		return mOwnerName;
	}
	
	public MagiCalBT getMagicalBT(){
		return magicBT;
	}
	
	public View getNextMonthDay(int id){
		return nextMonthDays.get(id);
	}

	public void addNextMonthDay(View v){
		nextMonthDays.add(v);
	}

	public boolean isNextMonthFull(){
		return nextMonthFull;
	}

	public void setNextMonthFull(boolean value){
		nextMonthFull = value;
	}

	public void clearNextMonthDays(){
		nextMonthDays.clear();
	}

	public boolean isThisMonthFull(){
		return thisMonthFull;
	}

	public void setThisMonthFull(boolean value){
		thisMonthFull = value;
	}

	public void addThisMonthDay(View v){
		thisMonthDays.add(v);
	}

	public void clearThisMonthDays(){
		thisMonthDays.clear();
	}

	public boolean isPrevMonthFull(){
		return prevMonthFull;
	}

	public void setPrevMonthFull(boolean value){
		prevMonthFull = value;
	}

	public void addPrevMonthDay(View v){
		prevMonthDays.add(v);
	}

	public void clearPrevMonthDays(){
		prevMonthDays.clear();
	}

	public int getPrevMonthSize(){
		return prevMonthDays.size();
	}

	public View getPrevMonthDay(int id){
		return prevMonthDays.get(id);
	}
	
	// Corresponding Calendar method is only available from later version of Android
	// Create custom one.
	private String getMonthName(int month){
		switch(month){
		case 0:
			return "January";
		case 1:
			return "February";
		case 2:
			return "March";
		case 3:
			return "April";
		case 4:
			return "May";
		case 5:
			return "June";
		case 6:
			return "July";
		case 7:
			return "August";
		case 8:
			return "September";
		case 9:
			return "October";
		case 10:
			return "November";
		case 11:
			return "December";
		default:
			return "Error";
		}
	}

}